package a5.grammar.productions;

import java.awt.Cursor;
import java.util.LinkedList;
import java.util.List;

import a5.common.OrderedTree;
import a5.common.ProgramCursor;
import a5.common.Token;
import a5.environment.Environment;
import a5.grammar.annotations.Grammar;
import a5.grammar.grammars.G1;
import a5.grammar.interfaces.Production;

/**
 * <pre>
 * This class provides a method for interpreting the following production
 * Application -> FunctionName ( {Expr} )
 * 
 * This production is composed of a {@link a5.grammar.productions.FunctionName FunctionName}
 * followed by a pair of parenthesis which may or may not contain a series of 
 * {@link a5.grammar.productions.Expr Expr}s.
 * 
 * This production uses recursive decent to interpret and parse.
 * </pre>
 * 
 * @author Jason Campos
 * @version Assignment 5, CS 152 (Programming Paradigms) Fall 2012, Jeffrey Smith
 */
@Grammar(clazz=G1.class)
public class Application implements Production{
	// Production dependencies
	private FunctionName functionName;
	private Expr expr;
	
	// Hide constructor
	private Application(){}
	
	/**
	 * Recursively interprets the program of the argument cursor using the variable/function scoping
	 * policies of the argument environment. 
	 * 
	 * @param environment
	 * The {@link a5.environment.Environment Environment} responsible for variable and function maintenance
	 * and scoping. 
	 * 
	 * @param cursor
	 * The {@link a5.common.ProgramCursor ProgramCursor} which contains the {@link a5.common.Token Tokens} 
	 * of the program being interpreted.
	 * 
	 * @return
	 * The {@code String} result of the {@link a5.function.Function Function} referenced by the result of  
	 * {@link a5.grammar.productions.FunctionName#interpret(Environment, Cursor) FunctionName.interpret()}.
	 */
	public String interpret(Environment environment, ProgramCursor cursor){
		// Use FunctionName.interpret() to load the name of the function to be invoked
		String function = functionName.interpret(environment, cursor);
		cursor.match(environment, "(");

		// Any Expr is an argument to the function referenced by functionName
		LinkedList<String> args = new LinkedList<>();
		while(expr.first(cursor))
			args.add(expr.interpret(environment, cursor));
		
		cursor.match(environment, ")");
		
		// Load the function from the environment and invoke it with the arguments 
		// loaded as a result of the while loop above.
		return environment.getFunction(function).invoke(args);
	}
	
	/**
	 * Recursively parses the program of the argument cursor using the variable/function scoping
	 * policies of the argument environment.
	 * 
	 * @param cursor
	 * The {@link a5.common.ProgramCursor ProgramCursor} which contains the {@link a5.common.Token Tokens} 
	 * of the program being interpreted.
	 * 
	 * @return
	 * An {@link a5.common.OrderedTree OrderedTree<Token>} representing the parse structure of the 
	 * the program of the argument cursor.
	 */
	public OrderedTree<Token> parse(ProgramCursor cursor){
		List<OrderedTree<Token>> children = new LinkedList<OrderedTree<Token>>();
		children.add(functionName.parse(cursor));
		children.add(new OrderedTree<Token>(new Token(cursor.match("("))));
		
		// Expr may be repeated indefinitely here.
		// Continue to add new Expr to the parse tree 
		// until Expr.first() returns false
		while(expr.first(cursor))
			children.add(expr.parse(cursor));
		
		children.add(new OrderedTree<Token>(new Token(cursor.match(")"))));
		return new OrderedTree<Token>(new Token(this.getClass().getSimpleName()), children);	
	}
	
	/**
	 * @param cursor
	 * The {@link a5.common.ProgramCursor ProgramCursor} which contains the {@link a5.common.Token Tokens} 
	 * of the program being interpreted.
	 * 
	 * @return
	 * {@code true} if the execution point of the program is at a point which indicates that
	 * an Application is a valid path of execution. In other words, if the current point of
	 * execution contains an element which is the first element of this production (FunctionName).
	 */
	public boolean first(ProgramCursor cursor) {
		return functionName.first(cursor);
	}
}
